feat: updating plivo imports - #122
Conversation
|
| Filename | Overview |
|---|---|
| app/services/telephony/recording_download.py | Separates credentialed carrier hosts from general recording hosts and validates literal IPs, DNS results, and redirects. |
| app/workers/tasks/process_call_import_row.py | Adds credentialed Plivo recording acquisition while preserving provider-specific concurrency and error handling. |
| app/services/telephony/carrier_media_serializer.py | Selects Plivo or Vobiz media serialization and resolves the selected Plivo integration鈥檚 call-control credentials. |
| app/services/telephony/inbound_stream_answer.py | Creates provider-tagged inbound call sessions and records the routing integration used by native Plivo calls. |
| app/services/telephony/call_recording_lifecycle.py | Replaces bounded in-memory call-record scans with dialect-aware JSON lookups and persists carrier metadata. |
| app/api/v1/routes/vobiz_telephony.py | Moves media streaming to the shared carrier endpoint and selects its serializer from the matched call record. |
| app/services/telephony/telephony_service.py | Routes native Plivo calls into streaming media callbacks and persists the selected integration for later call control. |
| app/services/telephony/number_import_service.py | Registers provider-specific webhook URLs and normalizes imported country and application metadata. |
Reviews (6): Last reviewed commit: "feat: updating telephony integration" | Re-trigger Greptile
| _CREDENTIALED_RECORDING_IMPORT_PROVIDERS = frozenset({"exotel", "plivo"}) | ||
|
|
||
|
|
||
| def _use_credentialed_recording_download(call_import, client) -> bool: | ||
| """True when CSV recording URLs should be fetched with provider auth.""" | ||
| if client is None or not hasattr(client, "download_recording"): | ||
| return False | ||
| return (call_import.provider or "").lower() == "exotel" | ||
| return (call_import.provider or "").lower() in _CREDENTIALED_RECORDING_IMPORT_PROVIDERS |
There was a problem hiding this comment.
Plivo credentials reach untrusted hosts
When a credentialed Plivo import contains a recording URL hosted on an attacker-controlled public IP address, the worker passes that URL to PlivoClient.download_recording, which attaches the account ID and token as HTTP Basic authentication. Literal public IP addresses bypass the carrier hostname-suffix allowlist, exposing the organization's Plivo credentials to the destination server. How this was verified: The imported row URL reaches the authenticated Plivo download helper, while URL validation accepts non-blocked literal public IP hosts without applying the carrier-host suffix allowlist.
Knowledge Base Used: Telephony provider workflows
| return None | ||
|
|
||
|
|
||
| _CREDENTIALED_RECORDING_IMPORT_PROVIDERS = frozenset({"exotel", "plivo"}) |
There was a problem hiding this comment.
Plivo credentials reach shared hosts
When a credentialed Plivo import contains a recording URL for an attacker-controlled S3 bucket or CloudFront distribution, the worker sends the organization's Plivo Basic-auth credentials because every subdomain of amazonaws.com and cloudfront.net is allowlisted, exposing those credentials to the destination. How this was verified: The CSV recording URL reaches the authenticated Plivo downloader, while the hostname validator accepts all subdomains beneath the shared Amazon and CloudFront suffixes.
Knowledge Base Used:
| serializer = build_carrier_frame_serializer( | ||
| provider_platform=getattr(call_row, "provider_platform", None), | ||
| stream_id=stream_id, | ||
| call_id=call_id, | ||
| auth_id=settings.VOBIZ_AUTH_ID, | ||
| auth_token=settings.VOBIZ_AUTH_TOKEN, | ||
| params=VobizFrameSerializer.InputParams( | ||
| sample_rate=8000, | ||
| api_base=settings.VOBIZ_API_BASE, | ||
| ), | ||
| organization_id=UUID(session.organization_id), | ||
| db=db, | ||
| telephony_integration_id=telephony_integration_id_from_call_row(call_row), |
There was a problem hiding this comment.
Plivo streams fall back to Vobiz
If more than 200 newer call records exist when a Plivo media WebSocket starts, the bounded call-reference lookup returns no row and this code passes None as the provider. The serializer factory then selects Vobiz, causing automatic hangups to target Vobiz while the Plivo call remains connected.
Knowledge Base Used:
What Changed?
Briefly describe what this PR changes.
Why?
Explain the problem this solves and why this approach was chosen.
How to Test?
List clear steps for reviewers to verify the change.
Release Label
Select one semantic version bump intent for this PR:
major- breaking change, next release bumps major versionminor- backward-compatible feature, next release bumps minor versionfix- backward-compatible bug fix, next release bumps patch versionIf you do not have permission to apply labels, mention the intended release label here and a maintainer will set it.
Checklist
CONTRIBUTING.mdguide.